#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "vm.h"
#include "dbg.h"


/*Loads a program into the brain10 vm. Once started,
 the instructions and data are loaded into memory.
 @param *vm pointer to a brain10 vm struct
 @param fileName the name of the file to load
 */
FILE* loadProgram(brain10 *vm, char fileName[], list* rdyQ, list* unloadedQ,
		list* freePages) {


	/* Find and open the file */
	FILE* file = fopen(fileName, "r");
	if (!file) {
		printf("Could not open file\n");
		exit(1);
	}



	/* before we start, we need to have a way of finding
	 * empty pages to store data into.
	 */
	int pid = 1;
	char buffer[100];
	fgets(buffer, 100, file); //load first line
	while (strncmp(buffer, "DATA", 4) != 0) {

		//create process struct to store each process
		process* proc = (process*) malloc(sizeof(process));
		snprintf(proc->PID, 3, "%.2i", pid); // save the PID


		/* the first line should be BRAIN10 */
		checkTitle(buffer);

		/* the second line should be the pages */
		fgets(buffer, 100, file);
		int pages = atoi(buffer);
		proc->pagesNeeded = pages; // save the pages requested

		fgets(buffer, 100, file); //get first proc instruction
		int ic = 0;
		while(strncmp(buffer, "BRAIN10", 7) != 0 &&
				strncmp(buffer, "DATA", 4) != 0 ){

			/* copy that into the proc struct */
			char* inst = (char*)malloc(sizeof(char)*5);
			strncpy(inst, buffer, 4);
			inst[5] = '\0';
			append(&proc->instructions, inst, 0);

			/* get the next instruction */
			fgets(buffer, 100, file);
			ic++;
		}

		proc->ic = ic; //save the number of instructions
		/* check to see if 0 >= requestedPages >= 10 */
		if(!checkPages(pages))
			append(unloadedQ, proc, 0); // add to the load queue
		pid++;
	}

	/* At this point, we have a list of every process that needs
	 * to be loaded stored inside of unloadedQ. Now we need to
	 * go through the queue and load all the processes into memory
	 * that will fit.
	 */
	loadProcesses(vm, rdyQ, unloadedQ, freePages);



	/* print out the loadq */
	//	int i;
	//	node* n = unloadedQ->head;
	//	process* pro = unloadedQ->head->data;
	//	for(i = 0; i < unloadedQ->length; i++){
	//
	//		int j;
	//		node* nn = pro->instructions.head;
	//		for(j = 0; j < pro->ic; j++){
	//
	//			printf("PROC %i: %s\n", i, (char*)nn->data);
	//			nn = nn->next;
	//		}
	//
	//		if(i < unloadedQ->length-1){
	//			n = n->next;
	//			pro = n->data;
	//		}
	//
	//	}










	//	exit(1);

	return file;
}

void loadProcesses(brain10 *vm, list* rdyQ, list* unloadedQ, list* freePages){

	if(unloadedQ->length == 0)
		return;

	int i;
	/* our data is a void*, so we need to access it
	 * through this casted process */
	node* proNode = unloadedQ->head;
	process* pro = (process*)proNode->data;
	int unloadedProcesses = unloadedQ->length;
	for(i = 0; i < unloadedProcesses; i++){

		if(pro->pagesNeeded < freePages->length){
#ifdef P4
			printf("==> Proc %s needs %i pages, %i are free\n", pro->PID,
					pro->pagesNeeded, freePages->length);
#endif
			/* create a pcb for the process */
			pcb* block = (pcb*) malloc(sizeof(pcb));
			initPCB(block);
			block->pt = createPageTable(10); // page table
			block->totalPages = pro->pagesNeeded; //useful for PD

			strcpy(block->PID, pro->PID);
			append(rdyQ, block, 0);  //append that pcb to the rdyQ


			/* find a page to use*/
			int j;
			int virtualPage = 0; //virtual page 0
			for(j = 0; j < pro->pagesNeeded; j++){

				/* get a page from the free page list */
				node* pageNode = pop(freePages);
				int targetFrame = *(int*)pageNode->data;

				/* record this page in the process page table */
				block->pt[virtualPage].frame = targetFrame;
				block->pt[virtualPage].val = 1;

				/* load instructions into that page */
				int k;
				for(k = 0; k < 10; k++){

					if(pro->instructions.length > 0){
						/* get the next instruction to load */
						char* inst = (char*)pro->instructions.head->data;
						strncpy(vm->memory[targetFrame][k],
								inst, 4);
#if defined(P4) && defined(DEBUG)
						//printf("Just put %s (vpage %i) in mem[%d][%d]\n", inst,
						//		virtualPage, targetFrame, k);
#endif
						/* free the used instruction */
						free(pop(&pro->instructions));

					}else{
						/* if there arn't enough instructions to fill out
						 * the rest of this page, then fill it with 0000 */
						sprintf(vm->memory[targetFrame][k], "0000");
#if defined(P4) && defined(DEBUG)
						//printf("Just put 0000 (vpage %i) in mem[%d][%d]\n",
						//		virtualPage, targetFrame, k);
#endif
					}

				}
#ifdef P4
//				printf("\n\n");
#endif
				virtualPage++;
				free(pageNode); //popped nodes must be freed


			}

			/* move onto the next process */
			proNode = proNode->next;
			free(pop(unloadedQ));
			if(i < unloadedProcesses-1) //null pointer protection
				pro = (process*)proNode->data;

		}else{
			/* if the process won't fit into memory, just put it
			 * at the end of the unloadedQ and keep checking the
			 * other process sizes.. one of them may fit. We do this
			 * because the list is in PID order, not pages-needed
			 * order...*/
#ifdef P4
			printf("Not enough room for PID %s\n",pro->PID);
#endif
			/* move onto the next process */
			proNode = proNode->next;
			cycle(unloadedQ);
			if(i < unloadedProcesses-1) //null pointer protection
				pro = (process*)proNode->data;
		}


	}
#ifdef P4
	printf("Remaining unloaded processes: %i\n", unloadedQ->length);
#endif

#ifdef P4
//	printPageTable(rdyQ);
#endif
}

/* project 4
 * prints out the page table of each process
 * in a given queue.
 * @param queue A list where the data is of type process
 */
void printPageTable(list* rdyQ){

	if(rdyQ->length == 0)
		return;

	/* print out the page tables of each process */
	int a;
	node* rnode = rdyQ->head;
	pcb* b = (pcb*)rdyQ->head->data;
	for(a = 0; a < rdyQ->length; a++){
		printf("Proc: %s\n", b->PID);

		int i;
		printf(" P | F |valid\n");
		printf("---+---+---\n");
		for(i = 0; i < 10; i++){

			printf("%3i|%3d|%3d\n",i, b->pt[i].frame, b->pt[i].val);
		}
		printf("\n");

		if(a < rdyQ->length-1){
			rnode = rnode->next;
			b = (pcb*)rnode->data;
		}

	}
}

/*store the context of a brain10 vm into a
 * skeleton process control block.
 * @param block The pcb to saved the process context into
 * @param vm The virtual machine to save the context of
 */
void storeContext(pcb* block, brain10* vm){
//	strcpy(block->R, vm->R); //copy the register
//	strcpy(block->IC, vm->IC); //copy the counter
//	strcpy(block->SP, vm->SP); //copy the stack pointer
//	strcpy(block->BR, vm->BR); //copy the base register
//	strcpy(block->LR, vm->LR); //copy the limit register
//	block->C = vm->C;
//	strcpy(block->PID, vm->PID); //copy the process id
}

/*load the context of a process control block
 * into a brain10 vm
 * @param block The pcb to load from
 * @param vm The virtual machine to load into
 */
void loadContext(brain10* vm, pcb* block){
//	strcpy(vm->R, block->R); //copy the register
//	strcpy(vm->IC, block->IC); //copy the counter
//	strcpy(vm->SP, block->SP); //copy the stack pointer
//	strcpy(vm->BR, block->BR); //copy the base register
//	strcpy(vm->LR, block->LR); //copy the limit register
//	vm->C = block->C;
//	strcpy(vm->PID, block->PID); //copy the process id

	/* project 4 */
	vm->pt = block->pt; //set the active page table
	vm->activePcb = block;

	vm->R = block->R;
	vm->IC = block->IC;
	vm->SP = block->SP;
	vm->BR = block->BR;
	vm->LR = block->LR;
	vm->C = &block->C;
	vm->PID = block->PID;
}

/*zero out all of the registers in a vm
 * @param vm The virtual machine to reset
 */
void reset(brain10*vm){ //think about deleting

	snprintf(vm->R,5, "0000");
	snprintf(vm->IC,3, "00");
	snprintf(vm->SP,5, "0000");
	snprintf(vm->BR,5, "0000");
	snprintf(vm->LR, 5, "0000");
	snprintf(vm->PID, 3, "00");
	*vm->C = '-';
	printf("GOT HERE\n");
}

int calcBR(int current){
	double d = current;
	d /= 100;
	current = (int)d;
	current++;
	current *= 100;

	return current;
}

/* project 3
 * Initialize the value of all of the semaphores
 */
void initSemaphores(brain10* vm, int value){
	int i;
	for(i=0; i < 100; i++){
		vm->semaphores[i].count = value;
	}
}


/* project 4
 * Translates an address from virtual to physical
 * memory.
 * @param address The address attempting to be accessed
 * @param pt Pagetable to use for lookup
 * @return -1 during a segault, esle the page frame
 * number
 */
int translateAddress(char* address, pageTable* pt){




	int x1 = address[0] - 48;
//	int x2 = address[1] - 48;

//	printf("x1: %i\n",x1);
//	printf("x2: %i\n",x2);

	/* 1 = valid
	 * 0 = invalid
	 */
	if(pt[x1].val == 0){
		printf("Process %s, segmentation fault\n", brain_vm->PID);
		HA();
		return -1;
	}

	return pt[x1].frame;
}

/* project 4
 * Simple helper function to check that the
 * requested pages is no higher than 10 and
 * no less than 0 (saves space)
 * @param pages The number to test
 */
int checkPages(int pages){
	if (pages < 0 || pages > 10) {
		printf("Processes requested %i pages\n", pages);
		printf("Processes can only request up to 10 pages\n");
		return 1;
	}
	return 0;
}

/* project 4
 * simple helper function to check that the
 * string equals "BRAIN10"
 * @param string The string to check
 */
void checkTitle(char* string){
	if (strncmp(string, "BRAIN10", 7)) {
		printf("The file format is not correct\n");
		printf("First line is %s, should be BRAIN10\n", string);
		exit(1);
	}
}

/*Dump the contents of the register to the screen
 @param *vm pointer to a brain10 vm struct
 @param n max words to print
 */
void print(brain10 *vm) {
	/*print out the registers*/

	printf("R   :    %s        ", vm->R);
	printf("SP  :    %s        ", vm->SP);
	printf("IC  :    %s\n", vm->IC);
	printf("BR  :    %s        ", vm->BR);
	printf("LR  :    %s\n", vm->LR);
	printf("C   :    %c        ", *vm->C);
	printf("PID :    %s\n\n", vm->PID);

}

/*load instruction
 @param *vm pointer to a virutal machine struct
 @param address address in memory between 0 and 99
 */
void LR(brain10 *vm, char* address) {
	//	printf("Instruction: %s\n", vm->memory[translateAddress(address,vm->pt)]
	//	         	                          [ address[1] - 48 ]);
	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;
	strncpy(vm->R, vm->memory[frame][offset], 4);
}

/*load instruction, low order bits
 @param *vm pointer to a virutal machine struct
 @param address address in memory between 0 and 99
 */
void LL(brain10 *vm, char* address) {



	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;

	int i;
	for (i = 2; i < 4; i++) {
		vm->R[i] = vm->memory[frame][offset][i];
	}

	printf("LL after %s\n",vm->R);

}

/* theory: store context is wrong. I can either fix
 * this or I can do the right thing and convert all of
 *
 */


/*load instruction, high order bits
 @param *vm pointer to a virutal machine struct
 @param address address in memory between 0 and 99
 */
void LH(brain10 *vm, char* address) {

	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;

	int i;
	for (i = 0; i < 2; i++) {
		vm->R[i] = vm->memory[frame][offset][i];
	}
}

/*Copy the contents of the register into
 address.
 @param *vm pointer to a virutal machine struct
 @param address address in memory between 0 and 99
 */
void SR(brain10 *vm, char* address) {
	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;

	strncpy(vm->memory[frame][offset], vm->R, 4);
}

/*Copy the contents of the register into SP
 @param *vm pointer to a virutal machine struct
 */
void SP(brain10 *vm) {
	strncpy(vm->SP, vm->R, 4);
}

/*Copy the contents of SP into the register
 @param *vm pointer to a virutal machine struct
 */
void PS(brain10 *vm) {
	strncpy(vm->R, vm->SP, 4);
}

/*Increment SP, then set the contents of the address
 SP contains to the contents of R
 @param *vm pointer to a virutal machine struct
 */
void PH(brain10* vm) {
	int sp = atoi(vm->SP);

	sp++;
	snprintf(vm->SP, 5, "%.4i", sp); //ADDED .4, changed 5

	char address[2] = {vm->SP[2], vm->SP[3]};

	int frame = translateAddress(address, vm->pt);
	int offset = address[1] - 48;

	strncpy(vm->memory[frame][offset], vm->R, 4);
}

/*Set R to the contents of the address SP contains,
 then decrement SP.
 @param *vm pointer to a virutal machine struct
 */
void PP(brain10* vm) {
	int sp = atoi(vm->SP);

	char address[2] = {vm->SP[2], vm->SP[3]};
	int frame = translateAddress(address, vm->pt);
	int offset = address[1] - 48;

	strncpy(vm->R, vm->memory[frame][offset], 4);

	sp--;
	snprintf(vm->SP, 5, "%.4i", sp);
}

/*If R is equal to the contents of address
 then set C to 'T', else 'F'
 @param *vm pointer to a virutal machine struct
 @param address address in memory between 0 and 99
 */
void CE(brain10 *vm, char* address) {
	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;

	if (strncmp(vm->R, vm->memory[frame][offset], 4)) {
		*vm->C = 'F';
	} else {
		*vm->C = 'T';
	}
}

/*If R is less than the contents of address
 then set C to 'T', else 'F'
 @param *vm pointer to a virutal machine struct
 @param address address in memory between 0 and 99
 */
void CL(brain10 *vm, char* address) {
	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;

	int i, j;
	i = atoi(vm->R);
	j = atoi(vm->memory[frame][offset]);

	if (i < j) {
		*vm->C = 'T';
	} else {
		*vm->C = 'F';
	}
}

/*If C is 'T' then IC is set to arg
 @param *vm pointer to a virutal machine struct
 @param arg two byte array that contains the arguments
 x1x2 in the initial instruction.
 */
int BT(brain10 *vm, char arg[]) {
	if (*vm->C == 'T') {
		strncpy(vm->IC, arg, 3);
		return 1;
	}
	return 0;
}

/*Set IC to arg
 @param *vm pointer to a virutal machine struct
 @param arg two byte array that contains the arguments
 x1x2 in the initial instruction.
 */
void BU(brain10 *vm, char arg[]) {
	strncpy(vm->IC, arg, 3);
}

/*Reads the next line from the input stream.
 @param *vm pointer to a brain10 vm struct
 @param arg two byte array that contains the arguments
 x1x2 in the initial instruction
 */
int GD(brain10 *vm, char* address, FILE *file) {

	printf("=======================\n");
	printf("GD\n");

	int totalPages = vm->activePcb->totalPages;
	if(atoi(address) > (totalPages*10)-10){
		printf("Process %s, segmentation fault\n", vm->PID);
		return 1;
	}



	char buffer[100], temp[5]; //I changed temp to a char instead of an int so that
	int counter = 0, c, i; //the strncpy would stop throwing warnings at us
	fgets(buffer, 100, file);



	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;



	/* copy the memory in the Data section into the
	 * specified location. Changed this to copy only
	 * as many spaces as the user specifies with data, instead
	 * of always copying 10.*/
	for (c = 0; buffer[counter] != '\0'; c++) {

		for (i = 0; i < 5; i++) {
			temp[i] = buffer[counter];
			counter++;
		}
		temp[4] = '\0';

		strncpy(vm->memory[frame][offset], temp, 5);

		offset++;
		if(offset > 9){
			offset = 0;
			frame++;
		}
	}

	return 0;
}

/*Print the contents of memory 10 at a time.
 @param *vm pointer to a brain10 vm struct
 @param start the location to start printing
 @return 1 if seg fault, else 0
 */
int PD(brain10 *vm, char* address) {

	int totalPages = vm->activePcb->totalPages;

	if(atoi(address) > (totalPages*10)-10){
		printf("Process %s, segmentation fault\n", vm->PID);
		return 1;
	}


	printf("\nPROC %s \n%4s:  ",vm->PID,address);

	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;

	/* the if in the middle serves to
	 * wrap the address from page to page, as each page
	 * only has a maximum of 10 instructions.
	 */
	int i, j=offset;
	for (i = 0; i < 10; i++) {
		printf("%s  ", vm->memory[frame][j]);

		j++;
		if(j == 9){
			j = 0;
			frame++;
		}
	}
	printf("\n\n");

	return 0;

}

/*Add the contents in the register and the memory at
 paramater location 'address' and then store it back
 into the Register
 @param *vm pointer to a brain10 vm struct
 @param address address in memory between 0 and 99
 */
void AD(brain10 *vm, char* address) {
	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;

	int r, a;
	a = atoi(vm->memory[frame][offset]);
	r = atoi(vm->R);
	a += r;
	snprintf(vm->R, 5, "%.4i", a);
}

/*Subtract the contents of the memory at the address
 location only if the Register is larger than the
 value in the memory and load that back into the Register
 @param *vm pointer to a brain10 vm struct
 @param address address in memory between 0 and 99
 */
void SU(brain10 *vm, char* address) {
	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;

	int r, a;
	a = atoi(vm->memory[frame][offset]);
	r = atoi(vm->R);
	if (r >= a) {
		r = r - a;
		snprintf(vm->R, 5, "%.4i", r);
	}
}

/*Multiply the contents in the register and the memory at
 paramater location 'address' and then store it back
 into the Register
 @param *vm pointer to a brain10 vm struct
 @param address address in memory between 0 and 99
 */
void MU(brain10 *vm, char* address) {
	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;

	int r, a;
	a = atoi(vm->memory[frame][offset]);
	r = atoi(vm->R);
	a *= r;
	snprintf(vm->R, 5, "%.4i", a);
}

/*Divide the contents in the register by the value in memory at
 paramater location 'address' and then store it back
 into the Register
 @param *vm pointer to a brain10 vm struct
 @param address address in memory between 0 and 99
 */
void DI(brain10 *vm, char* address) {
	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;

	int r, a;
	a = atoi(vm->memory[frame][offset]);
	r = atoi(vm->R);
	if (a > 0) {
		r = r / a;
		snprintf(vm->R, 5, "%.4i", r);
	}
}

//Add Stack
void AS(brain10 *vm) {

	char address[2] = {vm->SP[2], vm->SP[3]};
	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;

	int Temp = atoi(vm->memory[frame][offset]);
	sprintf(vm->SP, "%.4i", atoi(vm->SP) - 1);
	sprintf(vm->memory[frame][offset], "%.4i",
			atoi(vm->memory[frame][offset]) + Temp);

}

//Subtract Stack
void SS(brain10 *vm) {

	char address[2] = {vm->SP[2], vm->SP[3]};
	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;


	int Temp = atoi(vm->memory[frame][offset]);
	sprintf(vm->SP, "%.4i", atoi(vm->SP) - 1);
	sprintf(vm->memory[frame][offset], "%.4i",
			atoi(vm->memory[frame][offset]) - Temp);
}

//Multiply Stack
void MS(brain10 *vm) {

	char address[2] = {vm->SP[2], vm->SP[3]};
	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;

	int Temp = atoi(vm->memory[frame][offset]);
	sprintf(vm->SP, "%.4i", atoi(vm->SP) - 1);
	sprintf(vm->memory[frame][offset], "%.4i",
			atoi(vm->memory[frame][offset]) * Temp);
}

//Divide Stack
void DS(brain10 *vm) {

	char address[2] = {vm->SP[2], vm->SP[3]};
	int frame = translateAddress(address,vm->pt);
	int offset = address[1] - 48;


	int Temp = atoi(vm->memory[frame][offset]);
	sprintf(vm->SP, "%.4i", atoi(vm->SP) - 1);
	sprintf(vm->memory[frame][offset], "%.4i",
			atoi(vm->memory[frame][offset]) / Temp);
}

/* Helper function for the SD and RC instructions that
 * copies memory from one location to another based on
 * the BR and R of two pcb's / nodes.
 * @param vm the virtual machine where the memory resides
 * @param to the node containing the PCB that will be written to
 * @param from the node containing hte PCB that will be
 * read from
 * @param size the amount of words to copy
 */
void copy(brain10* vm, node* to, node* from, int size){

	pcb* toBlock = (pcb*)to->data;
	pcb* fromBlock = (pcb*)from->data;

	char toAddress[2] = {toBlock->R[2], toBlock->R[3]};
	int toFrame = translateAddress(toAddress,vm->pt);
	int toOffset = toAddress[1] - 48;

	char fromAddress[2] = {fromBlock->R[2], fromBlock->R[3]};
	int fromFrame = translateAddress(fromAddress,vm->pt);
	int fromOffset = fromAddress[1] - 48;

	int i, j=0;
	for(i = 0; i < size; i++){
		strcpy(vm->memory[toFrame][toOffset+j], vm->memory[fromFrame][fromOffset+j]);

		j++;
		if(j >9){
			toFrame++;
			fromFrame++;
			j = 0;
		}
	}

}


int SD(brain10* vm, list* sndQ, list* rcvQ, list* rdyQ, char targetPID []){

#ifdef DEBUG
	printf("CALLED SD\n");
#endif

	/* list of processes currently waiting to
	   receive a message from the sender    */
	pcb* rdyQBlock = (pcb*)rdyQ->head->data;
	list* q = &rcvQ[atoi(rdyQBlock->PID)];
	// check to see if anyone is waiting for this process to send
	int j;
	for(j = 0; j < 2; j++){

		if(q->length > 0){

			node* n = q->head;
			pcb* nBlock = (pcb*)n->data;
			int i;
			for(i = 0; i < q->length; i++){
				//see if any of them are the right ones to send to
				if( strcmp(nBlock->PID, targetPID) == 0 ){
#ifdef DEBUG
					printf("found process, sending\n");
#endif
//					int sndR = atoi(rdyQBlock->R);
//					int sndBR = atoi(rdyQBlock->BR);
//					int rcvR = atoi(nBlock->R);
//					int rcvBR = atoi(nBlock->BR);
//
//					int j;
//					for(j = 0; j < 10; j++){
//						strcpy(vm->memory[rcvR+rcvBR+j], vm->memory[sndR+sndBR+j]);
//					}

					copy(vm, rdyQ->head, n, 10);



					//remove the receiver from his queue
					append(rdyQ, 0, rm(q, i));

					//				cycle(rdyQ);
					return 0;
				}
				n = n->next; //compare with the next process in the queue
				nBlock = (pcb*)n->data;
			}

		}
		/* check the 'receive from all XX' queue, which is
		 * the 0 mailbox. Makes sure that processes that are waiting
		 * to receive specifically from this process are prioritized
		 */
		q = &rcvQ[0];

	}

#ifdef DEBUG
	pcb* tmp = (pcb*)rdyQ->head->data;
	printf("putting process %s into %s sndQ list \n", tmp->PID,targetPID);
#endif
	append(&sndQ[atoi(targetPID)], 0, pop(rdyQ) );
	return 1;
}

/* Receive a message from another process
 * @param vm A brain10 vm. Needed in order to copy
 * the message back into memory
 * @param sndQ An array of list types (defined in queue.h).
 * Each list represents a 'mailbox' for the process that corresponds
 * to the index it resides in. So sndQ[2] contains a list used
 * for managing every process that is currently waiting to send to
 * process two.
 * @param rcvQ An array of list types (defined in queue.h).
 * Each list represents a 'mailbox' for the process that corresponds
 * to the index it resides in. So rcvQ[1] contains a list used for
 * managing every process currently waiting to receive from p1
 * @param rdyQ A list of all currently ready and executing processes.
 * @param targetPID char array that contains the target process to
 * receive from, or XX if any process.
 */
int RC(brain10* vm, list* sndQ, list* rcvQ, list* rdyQ, char targetPID []){

#ifdef DEBUG
	printf("CALLED RC at %s\n",targetPID);
#endif

	/* list of processes currently waiting to
	   send a message to the receiver    */
	pcb* rdyQBlock = (pcb*)rdyQ->head->data;
	list* q = &sndQ[atoi(rdyQBlock->PID)];


	// check to see if anyone is trying to send to this process
	if(q->length > 0){

		node* n = q->head;
		pcb* nBlock = (pcb*)n->data;
		int i;
		for(i = 0; i < q->length; i++){

			//see if any of them are the right ones to receive from
			if( strcmp(nBlock->PID, targetPID) == 0 || strcmp(targetPID, "XX") == 0){
#ifdef DEBUG
				pcb* tmp = (pcb*)n->data;
				printf("matched %s with %s, i=%i\n",targetPID, tmp->PID,i);
#endif
				copy(vm, rdyQ->head, n, 10);

				//remove the sender from his queue
				append(rdyQ,  0, rm(q, i));
				//					cycle(rdyQ);
				return 0;
			}
			n = n->next; //compare with the next process in the queue
			nBlock = (pcb*)n->data;
		}
	}

	if( strncmp(targetPID, "XX",2) == 0 ){
#ifdef DEBUG
		printf("receiving from all, throwing into XX queue\n");
#endif
		append(&rcvQ[0], 0, pop(rdyQ) );
		return 1;
	}else{
		append(&rcvQ[atoi(targetPID)], 0, pop(rdyQ) );
		return 1;
	}

}

/* Loads the process ID into R
 * @param vm A brain10 vm to load the ID into
 */
void GP(brain10* vm){
	vm->R[0] = '0';
	vm->R[1] = '0';
	vm->R[2] = vm->PID[0];
	vm->R[3] = vm->PID[1];
}


/* Load Shared  -  project 3
 * loads memory from the shared memory segment
 * into the register
 * @param *vm pointer to a brain10 vm struct
 * @param address address in memory between 0 and 99
 */
void LS(brain10* vm, int address){
	strncpy(vm->R, vm->shared[address], 4);
}


/* Store Shared  - project 3
 * stores data from the register
 * into the shared memory segment
 * @param *vm pointer to a brain10 vm struct
 * @param address address in memory between 0 and 99
 */
void ST(brain10* vm, int address){
	strncpy(vm->shared[address], vm->R, 4);
}

/* added in project 3
 * print values from shared memory
 */
void printShared(brain10 *vm, int start) {

	printf("\nPROC %s \n%.4i:  ",vm->PID,start);
	int i;
	for (i = start; i < start + 10; i++) {
		printf("%s  ", vm->shared[i]);
	}
	printf("\n\n");

}

/* Wait  - poject 3
 * calls wait() on the semaphore specified
 * in the address field
 * @param *vm pointer to a brain10 vm struct
 * @param address address in memory between 0 and 99
 */
int PE(brain10* vm, int address){
	return wait(&vm->semaphores[address]);
}

/* Signal  - project 3
 * calls signal() on the semaphore specified
 * in the address field
 * @param *vm pointer to a brain10 vm struct
 * @param address address in memory between 0 and 99
 */
int VE(brain10* vm, int address){
	sig(&vm->semaphores[address]);
	return 0;
}

/* Semaphore Initialize  -  project 3
 * initializes a semaphore value
 * @param *vm pointer to a brain10 vm struct
 * @param address address in memory between 0 and 99
 */
void SI(brain10* vm, int address){
	vm->semaphores[address].count = address;
}

/* added in project 3
 * this function needs to be called in order to use
 * semaphores. It tells the semaphores which ready queue
 * they are receiving waits and signals from, and where
 * processes should be placed when they are removed from a
 * semaphore queue.
 * @param readyQ The queue that semaphores return processes
 * to.
 */
void setReadyQ(list* readyQ){
	process_ready_queue = readyQ;
}

/* project 4
 * End a process by removing it from the ready queue and
 * releasing the frames that it held.
 * @param vm The brain10 VM
 * @param rdyQ The list of processes that are currently executing
 */
void HA(){

	node* proc = pop(brain_rdyQ);
	pcb* block = (pcb*) proc->data;

#ifdef P4
	printf("Process %s exiting\n", block->PID);
	int prevFrees = brain_freePages->length;
#endif



	int i, frame;
	for(i = 0; i < 10; i++){ //10 is the size of the page table

		/* free all of the pages currently in use */
		if(block->pt[i].val == 1){
#ifdef P4
//			printf("freeing virtual page %d\n",i);
#endif
			int j;
			for(j = 0; j < 10; j++){ //10 instruction in a page that need freeing
				frame = block->pt[i].frame;
				sprintf(brain_vm->memory[frame][j], "0000");
			}

			int* freePage = malloc(sizeof(int));
			*freePage = block->pt[i].frame;
			append(brain_freePages, freePage, 0);

#ifdef P4
			int* d = brain_freePages->tail->data;
			printf("adding physical frame %d\n", *d);
#endif
		}


	}
#ifdef P4
	printf("Free frames increased from %i to %i\n\n", prevFrees, brain_freePages->length);
#endif

	free(proc);

	/* load any remaining processes that fit into memory */
	loadProcesses(brain_vm, brain_rdyQ, brain_unloadedQ, brain_freePages);

	halted = 1;
}








